#include "base.h"
#include "memreuse.h"
#include "hashtable.h"
#include "mpz_tool.h"

// bits=28
void HashTable::Create(unsigned int bits)
{
    /* Check requested hashtable isn't too large, < 10G */
  unsigned int size = 1u<<bits;
  unsigned bytesize = sizeof(struct entry*) * size;
  //cout<<sizeof(struct entry*)<<" "<<sizeof(struct entry)<<" "<<bytesize<<endl;
  assert(bytesize < 10*(1u<<30));

  m_pTable = new struct entry *[size];

  if (NULL == m_pTable) { return; }
  memset(m_pTable, 0, size * sizeof(struct entry *));
  m_uiTableLength = size;

  m_mpzMask = Memreuse::New();
  mpz_set_ui (*m_mpzMask, size-1);
  m_uiSize = size;
  m_uiCollisions = 0;
  m_uiTotal = 0;
}

int HashTable::Insert(mpz_t * mpzHashKi, string strEncK_Ki, int index)
{
  assert(mpzHashKi != NULL);
  // take the last 25 bits from mpzHashKi
  mpz_t * mpzKey = Memreuse::New();
  mpz_and (*mpzKey, *mpzHashKi, *m_mpzMask);
  unsigned long key = mpz_get_ui (*mpzKey);
  assert(key < m_uiSize);
  Memreuse::Delete(mpzKey);

  struct entry * e = new (struct entry);
  if (e == NULL)
	cerr<<"e=NULL"<<endl;
  e->mpzHashKi = mpzHashKi;
  e->strEncK_Ki = strEncK_Ki;
  e->index = index;
  e->next = NULL;

  if (m_pTable[key])
    m_uiCollisions++;

  struct entry * pos1 = m_pTable[key];
  struct entry * pos2 = m_pTable[key];
  // pos2 stops once bigger than mpzHashKi

  while (pos2 != NULL && mpz_cmp(*mpzHashKi, *(pos2->mpzHashKi)) > 0) // the database is encrypted from large to small and it is stored from small to large
  {
	pos1 = pos2;
	pos2 = pos2->next;
  }

  if (pos2 == m_pTable[key])
	{
   	  e->next = pos2;
   	  m_pTable[key] = e;
	}
  else
	{
	  pos1->next = e;
	  e->next = pos2;
	}
  m_uiTotal++;
  return 0;
}

struct entry* HashTable::Lookup(const struct entry* ie, mpz_t * mpzHashKi)
{
  struct entry * e;
  if (ie == NULL)
	{
	  mpz_t * mpzKey = Memreuse::New();
	  mpz_and (*mpzKey, *mpzHashKi, *m_mpzMask);
	  unsigned long key = mpz_get_ui (*mpzKey);
	  Memreuse::Delete(mpzKey);
	  e = m_pTable[key];
	}
  else
	e = ie->next;

  int n;
  while (NULL != e && (n = mpz_cmp (*mpzHashKi, *(e->mpzHashKi))) >= 0)
    {
	  if (n == 0)
        {
          return e;
        }
      e = e->next;
    }
  return NULL;
}

struct entry* HashTable::Lookup(const struct entry* ie, string strHashKi)
{
  mpz_t * mpzHashKi = Memreuse::New();
  MpzTool::Mpz_from_string(mpzHashKi, strHashKi);
  struct entry * ret = Lookup(ie, mpzHashKi);
  Memreuse::Delete(mpzHashKi);
  return ret;
}
